package org.wheel.plugins.toolkit.util.sql.visitor;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import lombok.Getter;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.wheel.module.core.request.ParamField;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 参数位置访问者，用于记录不同SQL模式下参数的位置信息
 * 提取原SQL中的参数表达式
 *
 */
public class ParameterPositionVisitor extends ExpressionVisitorAdapter<Void> {

    // 直接使用 LogFactory 创建日志对象，Hutool 会自动检测项目中的日志框架（优先级：Slf4j > Log4j > Logback > JDK Logging）
    private static final Log log = LogFactory.get();

    // 当前的字段名
    private String currentColumnName;
    // 操作连接符
    private String operatorConnect;

    // 参数索引
    private final AtomicInteger parameterIndex;

    @Getter
    private final List<ParamField> conditions;

    public ParameterPositionVisitor(){
        conditions = new ArrayList<>();
        this.parameterIndex = new AtomicInteger(0);
    }

    /**
     * 内部值设置
     * @param value
     */
    private void setConditions(Object value){
        if(conditions.size()<parameterIndex.get()){
            ParamField whereField = ParamField.builder()
                    .field(this.currentColumnName)
                    .connector(this.operatorConnect)
                    .value(value)
                    .skip(false)
                    .func(null)
                    .sno(conditions.size())
                    .build();
            conditions.add(whereField);
        }else{
            ParamField fields = conditions.get(parameterIndex.get()-1);
            // 历史参数
            Object val = fields.getValue();
            List<Object> list = new ArrayList<>();
            if(val instanceof Collection<?> collection){
                list.addAll(collection);
            }else{
                list.add(val);
            }
            list.add(fields.getValue());
            fields.setValue(list);
        }
    }

    @Override
    public <S> Void visit(Column column, S context)  {
        // Column是叶子节点，不需要进一步遍历
        int index = parameterIndex.incrementAndGet();
        log.debug("current index: {}, Found column: {}", index, column.getColumnName());
        this.currentColumnName = column.getColumnName();
        return null;
    }

    @Override
    public <S> Void visit(JdbcNamedParameter jdbcNamedParameter, S context) {
        log.debug("Found JdbcNamedParameter: " + jdbcNamedParameter.getName());
        log.debug("current index: {}, Column {},  Connector: {}, Value: {}", parameterIndex.get(), this.currentColumnName, this.operatorConnect, jdbcNamedParameter.toString());
        // 这里主要是要提取参数名称，所以不能完全以Column名称来提取参数名称
        Set<String> params = SqlParameterReplacementHandler.extractNamedParameters(jdbcNamedParameter.toString());
        if(params.size() == 1){
            this.currentColumnName = params.iterator().next();
        }
        setConditions(jdbcNamedParameter);

        return null;
    }

    @Override
    public <S> Void visit(JdbcParameter parameter, S context) {
        // JdbcParameter是叶子节点，不需要进一步遍历
        log.debug("Found JdbcParameter: " + parameter.toString());
        log.debug("current index: {},, Column {},  Connector: {}, Value: {}", parameterIndex.get(), this.currentColumnName, this.operatorConnect, parameter);
        // 这里主要是要提取参数名称，所以不能完全以Column名称来提取参数名称
        Set<String> params = SqlParameterReplacementHandler.extractNamedParameters(parameter.toString());
        if(params.size() == 1){
            this.currentColumnName = params.iterator().next();
        }
        setConditions(parameter);
        return null;
    }

    @Override
    public <S> Void visit(EqualsTo equalsTo, S context) {
        equalsTo.getLeftExpression().accept(this, context);
        equalsTo.getRightExpression().accept(this, context);
        log.debug("Found EqualsTo: " + equalsTo.getStringExpression());
        this.operatorConnect = equalsTo.getStringExpression();
        return null;
    }

    @Override
    public <S> Void visit(NotEqualsTo notEqualsTo, S context) {
        notEqualsTo.getLeftExpression().accept(this, context);
        notEqualsTo.getRightExpression().accept(this, context);
        log.debug("Found NotEqualsTo: " + notEqualsTo.getStringExpression());
        this.operatorConnect = notEqualsTo.getStringExpression();
        return null;
    }

    @Override
    public <S> Void visit(GreaterThan greaterThan, S context) {
        greaterThan.getLeftExpression().accept(this, context);
        greaterThan.getRightExpression().accept(this, context);
        log.debug("Found GreaterThan: " + greaterThan.getStringExpression());
        this.operatorConnect = greaterThan.getStringExpression();
        return null;
    }

    @Override
    public <S> Void visit(GreaterThanEquals greaterThanEquals, S context) {
        greaterThanEquals.getLeftExpression().accept(this, context);
        greaterThanEquals.getRightExpression().accept(this, context);
        log.debug("Found GreaterThanEquals: " + greaterThanEquals.getStringExpression());
        this.operatorConnect = greaterThanEquals.getStringExpression();
        return null;
    }

    @Override
    public <S> Void visit(MinorThan minorThan, S context) {
        minorThan.getLeftExpression().accept(this, context);
        minorThan.getRightExpression().accept(this, context);
        log.debug("Found MinorThan: " + minorThan.getStringExpression());
        this.operatorConnect = minorThan.getStringExpression();
        return null;
    }

    @Override
    public <S> Void visit(MinorThanEquals minorThanEquals, S context) {
        minorThanEquals.getLeftExpression().accept(this, context);
        minorThanEquals.getRightExpression().accept(this, context);
        log.debug("Found MinorThanEquals: " + minorThanEquals.getStringExpression());
        this.operatorConnect = minorThanEquals.getStringExpression();
        return null;
    }

    @Override
    public <S> Void visit(LikeExpression likeExpression, S context) {
        likeExpression.getLeftExpression().accept(this, context);
        likeExpression.getRightExpression().accept(this, context);
        log.debug("Found LikeExpression: " + likeExpression.getStringExpression());
        this.operatorConnect = likeExpression.getStringExpression();
        return null;
    }

    @Override
    public <S> Void visit(InExpression inExpression, S context) {
        inExpression.getLeftExpression().accept(this, context);
        inExpression.getRightExpression().accept(this, context);
        log.debug("Found InExpression: in ");
        this.operatorConnect = "in";
        return null;
    }

    @Override
    public <S> Void visit(Between between, S context) {
        log.debug("Found Between: BETWEEN");
        this.operatorConnect = "BETWEEN";
        between.getLeftExpression().accept(this, context);
        between.getBetweenExpressionStart().accept(this, context);
        between.getBetweenExpressionEnd().accept(this, context);
        return null;
    }

    @Override
    public <S> Void visit(AndExpression andExpression, S context) {
        andExpression.getLeftExpression().accept(this, context);
        andExpression.getRightExpression().accept(this, context);
        log.debug("Found AndExpression: " + andExpression.getStringExpression());
        this.operatorConnect = andExpression.getStringExpression();
        return null;
    }

    @Override
    public <S> Void visit(OrExpression orExpression, S context) {
        orExpression.getLeftExpression().accept(this, context);
        orExpression.getRightExpression().accept(this, context);
        log.debug("Found OrExpression: " + orExpression.getStringExpression());
        this.operatorConnect = orExpression.getStringExpression();
        return null;
    }

    @Override
    public <S> Void visit(LongValue longValue, S context) {
        // LongValue是叶子节点，不需要进一步遍历
        return null;
    }

    @Override
    public <S> Void visit(DoubleValue doubleValue, S context) {
        // DoubleValue是叶子节点，不需要进一步遍历
        return null;
    }

    @Override
    public <S> Void visit(StringValue stringValue, S context) {
        // StringValue是叶子节点，不需要进一步遍历
        return null;
    }

    @Override
    public <S> Void visit(DateValue dateValue, S context) {
        // DateValue是叶子节点，不需要进一步遍历
        return null;
    }

    @Override
    public <S> Void visit(TimestampValue timestampValue, S context) {
        // TimestampValue是叶子节点，不需要进一步遍历
        return null;
    }

    @Override
    public <S> Void visit(NullValue nullValue, S context) {
        // NullValue是叶子节点，不需要进一步遍历
        return null;
    }

    @Override
    public <S> Void visit(Function function, S context) {
        log.debug("Found Function: " + function.getName());
        // 可能是查询函数，也可能是在group 或者having中
        this.operatorConnect = null;
        // 处理函数表达式中的参数
        if (function.getParameters() != null) {
            function.getParameters().accept(this, context);
        }else if(function.getNamedParameters() != null){
            function.getNamedParameters().accept(this, context);
        }
        return null;
    }

    @Override
    public <S> Void visit(IntervalExpression intervalExpression, S context) {
        log.debug("Found IntervalExpression: " + intervalExpression.getExpression());
        if(intervalExpression.getExpression() != null) {
            intervalExpression.getExpression().accept(this, context);
        }
        return null;
    }

    //    @Override
//    public <S> Void visit(CaseExpression caseExpression, S context) {
//        log.debug("Found CaseExpression: CASE");
//        // 处理CASE表达式
//        if (caseExpression.getSwitchExpression() != null) {
//            caseExpression.getSwitchExpression().accept(this, context);
//        }
//
//        if (caseExpression.getWhenClauses() != null) {
//            for (WhenClause whenClause : caseExpression.getWhenClauses()) {
//                whenClause.accept(this, context);
//            }
//        }
//
//        if (caseExpression.getElseExpression() != null) {
//            caseExpression.getElseExpression().accept(this, context);
//        }
//        return null;
//    }
//
//    @Override
//    public <S> Void visit(WhenClause whenClause, S context) {
//        log.debug("Found WhenClause: WHEN");
//        whenClause.getWhenExpression().accept(this, context);
//        whenClause.getThenExpression().accept(this, context);
//        return null;
//    }

    @Override
    public <S> Void visit(ExistsExpression existsExpression, S context) {
        log.debug("Found ExistsExpression: {}", existsExpression.getStringExpression());
        this.operatorConnect = existsExpression.getStringExpression();
        Expression right = existsExpression.getRightExpression();
        if (right instanceof ParenthesedSelect) {
            ((ParenthesedSelect) right).getPlainSelect().getWhere().accept(this, context);
        } else {
            existsExpression.getRightExpression().accept(this, context);
        }
        return null;
    }

    @Override
    public <S> Void visit(ExpressionList<?> expressionList, S context) {
        for (Expression expr : expressionList) {
            expr.accept(this, context);
        }
        return null;
    }


}
